"""
将AST返回成code

author:
"""

from .core.dslc_parser import ASTNode, BlockOp, UnaryOp, BinaryOp, Identifier, Number, String
from .passes.dslc_var_define_pass import VAR_KWS


def unparsing_dslc(top: BlockOp):
    '''返回成dslc'''
    ofil = open('tmp/unparsing.txt', 'w')
    for op in top.oplist:
        ofil.write(__unparsing_dslc(op))
        ofil.write('\n')


def __unparsing_dslc(op: ASTNode):
    '''将结果输出成字符串'''
    if isinstance(op, BlockOp):
        cont = ['\nbegin']
        for _op in op.oplist:
            cont.append(__unparsing_dslc(_op) + ';')
        cont.append('end\n')
        return '\n'.join(cont)
    if isinstance(op, UnaryOp):
        return f'{op.x} {__unparsing_dslc(op.x)}'
    if isinstance(op, BinaryOp):
        return f'{__unparsing_dslc(op.l)} {op.op} {__unparsing_dslc(op.r)}'
    if isinstance(op, Identifier):
        return f'{op.name}'
    if isinstance(op, Number):
        return f'{op.value}'
    if isinstance(op, String):
        return f'\"{op.value}\"'


def unparsing_ccode(top: BlockOp):
    ''''''
    '''返回成dslc'''
    ofil = open('tmp/unparsing_c.c', 'w')
    for op in top.oplist:
        ofil.write(__unparsing_ccode(op))
        ofil.write('\n')


def __unparsing_ccode(op: ASTNode, flatten_block=False, indent=0, pre_cont=None):
    '''将结果输出成字符串'''
    pad = '    ' * indent
    if isinstance(op, BlockOp):
        env = []
        opslen = len(op.oplist)
        opidx = -1
        while True:
            #无论如何让opidx +1，从而肯定能到下一个而不是被while挡住，导致opidx停留在unary
            opidx += 1
            if opidx >= opslen:
                break
            _op = op.oplist[opidx]
            if isinstance(_op, UnaryOp) and _op.op == ':':
                if isinstance(_op.x, Identifier):
                    env.append(_op.x.name)
                elif isinstance(_op.x, BinaryOp):
                    env.append(_op.x.l.name+"::"+_op.x.r.value)
                else:
                    raise ValueError('can not parse COLON UnaryOp')
            else:
                break
        #print(opidx)
        cont = []
        if pre_cont:
            cont.extend(pre_cont)
        #处理env中的函数命名
        for e in env:
            #print(e)
            if e.startswith('cfun'):
                funname = e.split('::')[1]
                cont.append(funname)
        #处理完env开始处理内容
        if flatten_block:
            cont.append(', '.join([__unparsing_ccode(_op, indent=0) for _op in op.oplist[opidx:]]))
            return cont[-1]
        #如果是for block
        elif isinstance(op.oplist[0], Identifier) and op.oplist[0].name == 'for':
            cont.append(f'{pad}for ({__unparsing_ccode(op.oplist[1], flatten_block=True, indent=0)})')
            cont = __unparsing_ccode(op.oplist[2], indent=indent, pre_cont=cont)
            return cont
        #如果是while block
        elif isinstance(op.oplist[0], Identifier) and op.oplist[0].name == 'while':
            cont.append(f'{pad}while ({__unparsing_ccode(op.oplist[1], flatten_block=True, indent=0)})')
            cont = __unparsing_ccode(op.oplist[2], indent=indent, pre_cont=cont)
            return cont
        #如果是if block
        elif isinstance(op.oplist[0], Identifier) and op.oplist[0].name == 'if':
            cont.append(f'{pad}if ({__unparsing_ccode(op.oplist[1], flatten_block=True, indent=0)})')
            cont = __unparsing_ccode(op.oplist[2], indent=indent, pre_cont=cont)
            return cont
        #如果是else if block
        elif isinstance(op.oplist[0], Identifier) and op.oplist[0].name == 'else if':
            cont.append(f'{pad}else if ({__unparsing_ccode(op.oplist[1], flatten_block=True, indent=0)})')
            cont = __unparsing_ccode(op.oplist[2], indent=indent, pre_cont=cont)
            return cont
        #如果是else block
        elif isinstance(op.oplist[0], Identifier) and op.oplist[0].name == 'else':
            cont.append(f'{pad}else')
            cont = __unparsing_ccode(op.oplist[1], indent=indent, pre_cont=cont)
            return cont
        else:
            if len(cont) > 0:
                cont[-1] += f'{{'
            else:
                cont.append(f'\n{pad}{{')
            for _op in op.oplist[opidx:]:
                #print('REMIAN OP', _op, opidx)
                need_ending = True
                if isinstance(_op, BlockOp):
                    need_ending = False
                if isinstance(_op, String) and _op.strong:
                    need_ending = False
                ending = ';' if need_ending else ''
                cont.append(__unparsing_ccode(_op, indent=indent+1) + ending)
            cont.append(f'{pad}}}')
            return '\n'.join(cont)
    if isinstance(op, UnaryOp):
        optail = op.op.split(' ')[-1]
        if optail in VAR_KWS:
            return f'{pad}{op.op} {__unparsing_ccode(op.x, flatten_block=True, indent=0)}'
        if optail == 'RETURN':
            return f'{pad}return {__unparsing_ccode(op.x, flatten_block=True, indent=0)}'
        if optail == 'PTR':
            return f'{pad}*{__unparsing_ccode(op.x)}'
        return f'{pad}{op.op} {__unparsing_ccode(op.x)}'
    if isinstance(op, BinaryOp):
        #print(op)
        if op.op == '!':
            return f'{pad}{__unparsing_ccode(op.l)}({__unparsing_ccode(op.r, flatten_block=True)})'
        if op.op == '@':
            return f'{pad}({__type_cast_head(op.r)})({op.l.name})'
        else:
            return f'{pad}{__unparsing_ccode(op.l)} {op.op} {__unparsing_ccode(op.r)}'
    if isinstance(op, Identifier):
        return f'{pad}{op.name}'
    if isinstance(op, Number):
        return f'{pad}{op.value}'
    if isinstance(op, String):
        if op.strong:
            return f'{op.value}'
        return f'{pad}\"{op.value}\"'


def __type_cast_head(ast: ASTNode):
    if isinstance(ast, Identifier):
        return ast.name
    if isinstance(ast.x, String) and ast.x.value == 'Blank':
        return ast.op
    if isinstance(ast.x, BlockOp) and isinstance(ast.x.oplist[0], UnaryOp) and ast.x.oplist[0].op == 'PTR':
        return ast.op + '*'
    raise ValueError(f'__type_cast_head fail {ast}')
